package org.acghub.mtdb.core;

import lombok.extern.slf4j.Slf4j;
import org.acghub.mtdb.core.log.LogSimple;
import org.acghub.mtdb.core.log.impl.*;

import java.util.*;

@Slf4j
@SuppressWarnings("unchecked")
public final class Logs {
    public static void simple(TBean bean, String varName, Object wrapped, Runnable verify) {
        LogKey logKey = new LogKey(bean, varName);
        SavePoint sp = Transaction.currentSavepoint();
        LogSimple log = sp.get(logKey);
        if (log == null) {
            sp.add(logKey, new LogSimple(logKey, wrapped));
        } else if (log.isEqual(wrapped)) {
            log.rollback();
            sp.remove(logKey);
        }
    }

    public static <E> List<E> vector(TBean bean, String varName, List<E> wrapped, Runnable verify) {
        LogKey key = LogKey.of(bean, varName);
        Map<LogKey, Object> wrappers = Transaction.current().wrappers;
        LogArrayList<E> log = (LogArrayList<E>) wrappers.computeIfAbsent(key, k -> new LogArrayList<>(key, wrapped));
        log.setVerify(verify);
        return log;
    }

    public static <E> List<E> list(TBean bean, String varName, List<E> wrapped, Runnable verify) {
        LogKey key = LogKey.of(bean, varName);
        Map<LogKey, Object> wrappers = Transaction.current().wrappers;
        LogLinkedList<E> log = (LogLinkedList<E>) wrappers.computeIfAbsent(key, k -> new LogLinkedList<>(key, wrapped));
        log.setVerify(verify);
        return log;
    }

    public static <E> Deque<E> deque(TBean bean, String varName, Deque<E> wrapped, Runnable verify) {
        LogKey key = LogKey.of(bean, varName);
        Map<LogKey, Object> wrappers = Transaction.current().wrappers;
        LogDeque<E> log = (LogDeque<E>) wrappers.computeIfAbsent(key, k -> new LogDeque<>(key, wrapped));
        log.setVerify(verify);
        return log;
    }

    public static <E> Set<E> set(TBean bean, String varName, Set<E> wrapped, Runnable verify) {
        LogKey key = LogKey.of(bean, varName);
        Map<LogKey, Object> wrappers = Transaction.current().wrappers;
        LogHashSet<E> log = (LogHashSet<E>) wrappers.computeIfAbsent(key, k -> new LogHashSet<>(key, wrapped));
        log.setVerify(verify);
        return log;
    }

    public static <E> NavigableSet<E> treeset(TBean bean, String varName, NavigableSet<E> wrapped, Runnable verify) {
        LogKey key = LogKey.of(bean, varName);
        Map<LogKey, Object> wrappers = Transaction.current().wrappers;
        LogTreeSet<E> log = (LogTreeSet<E>) wrappers.computeIfAbsent(key, k -> new LogTreeSet<>(key, wrapped));
        log.setVerify(verify);
        return log;
    }

    public static <K, V> Map<K, V> map(TBean bean, String varName, Map<K, V> wrapped, Runnable verify) {
        LogKey key = LogKey.of(bean, varName);
        Map<LogKey, Object> wrappers = Transaction.current().wrappers;
        LogHashMap<K, V> log = (LogHashMap<K, V>) wrappers.computeIfAbsent(key, k -> new LogHashMap<>(key, wrapped));
        log.setVerify(verify);
        return log;
    }

    public static <K, V> NavigableMap<K, V> treemap(TBean bean, String varName, NavigableMap<K, V> wrapped, Runnable verify) {
        LogKey key = LogKey.of(bean, varName);
        Map<LogKey, Object> wrappers = Transaction.current().wrappers;
        LogTreeMap<K, V> log = (LogTreeMap<K, V>) wrappers.computeIfAbsent(key, k -> new LogTreeMap<>(key, wrapped));
        log.setVerify(verify);
        return log;
    }


    public static void link(Object o, TBean parent, String varname, boolean log) {
        if (Objects.nonNull(o) && (o instanceof TBean)) {
            ((TBean) o).link(parent, varname, log);
        }
    }

    public static void link(Object bean, TBean parent, String varname) {
        link(bean, parent, varname, true);
    }
}
